<?php
/**
 * In this file the class '\BF\Locale' is defined.
 *
 * @author   Kado <ulfikado@gmail.com>
 * @category Beluga PHP5.3+ Framework
 * @package  BF\Core
 * @since    2015-01-17 14:29
 * @version  0.1
 */

namespace BF
{


   /**
    * include the \BF\LocaleHalper class
    */
   require_once __DIR__ . '/LocaleHelper.class.php';

   /**
    * include the \BF\ExplicitGetter class
    */
   require_once __DIR__ . '/ExplicitGetter.class.php';


   /**
    * Defines the Locale class as a singleton.
    *
    * The automatic initialization works like follow:
    *
    * There are 5 different ways inside the class logic, used to initialize the locale data:
    *
    * <b style="color: red">1. By URL path</b>
    *
    * If the URL starts with a path element, usable as a LCID (Language Country ID) it will be used.
    * e.g. <code style="color: blue">de_DE</code> or <code style="color: blue">en-US</code> or also
    * <code style="color: blue">fr</code>.
    *
    * <b>This option is only used if {@see \BF\FrameworkConfig::GetInstance()}->Locale->InitFromUrlPath is enabled (TRUE)</b>.
    *
    * <b style="color: red">2. By Request data</b>
    *
    * If there is a GET, POST, SESSION or COOKIE parameter with the name from {@see \BF\FrameworkConfig::GetInstance()}->Locale->RequestDataParamName
    * is defined, so it will use the parameter value. e.g. <code>example.com/?lang=de_DE</code> or <code>example.com/?lang=fr</code>.
    *
    * <b>This option is only used if {@see \BF\FrameworkConfig::GetInstance()}->Locale->InitFromRequestData is enabled (TRUE)</b>.
    *
    * <b style="color: red">3. By ACCEPT_LANGUAGE Webbrowser info</b>
    *
    * If <code>$_SERVER['HTTP_ACCEPT_LANGUAGE']</code> is defined, the locale infos will be extracted from it.
    *
    * <b>This option is only used if {@see \BF\FrameworkConfig::GetInstance()}->Locale->InitFromAcceptLanguage is enabled (TRUE)</b>.
    *
    * <b style="color: red">4. By a fallback locale</b>
    *
    * If {@see \BF\FrameworkConfig::GetInstance()}->Locale->FallbackLocaleString is defined and is a valid locale definition, a
    * Locale instance is created from it.
    *
    * <b style="color: red">5. System locale</b>
    *
    * If the options before dont give some usable locale settings, the defined system locales is used. But it must
    * not finish successfull. Maybe if locale is 'C', it fails.
    *
    * <b style="color: red">6. Internal</b>
    *
    * If all other 5 options to get locale from fails, Language 'en' is used.
    *
    * <b>Usage</b>
    *
    * Its really easy to use this class. Mainly you only have to call {@see \BF\Locale::GetInstance()} If no locale
    * is defined it will be created, by one of the options (the first with success), described before.
    *
    * If you want to configure the way to get the used locale doit like this:
    *
    * <code>
    * // Get the global framework config
    * $conf = \BF\FrameworkConfig::GetInstance()
    *
    * // Set the Locale, used as Fallback, if no other option founds a usable locale
    * $conf->Locale->FallbackLocaleString = 'de_DE';
    *
    * // Disable the locale init from URL paths, if it is not needed. (see Field description)
    * $conf->Locale->InitFromUrlPath = false;
    *
    * // Set Name of Request paramer that is used by this option for getting the locale.
    * $conf->Locale->RequestDataParamName = 'lang';
    *
    * // Disable the locale init from Request data, if you dont need it (see Field description)
    * $conf->Locale->InitFromRequestData = false;
    *
    * // Disable the locale init from $_SERVER['HTTP_ACCEPT_LANGUAGE'] if is it no need for it
    * $conf->Locale->InitFromAcceptLanguage = false;
    * </code>
    *
    * All the configs above have to be done, if {@see \BF\Locale::GetInstance()} is called the first time by you.
    * The best way to do it is, set it from your own bootstrap project file.
    *
    * If you need the full locale string simple cast the object to a string
    * <code style="color:blue">(string) \BF\Locale::GetInstance()</code>
    *
    * @property-read string $LID      The current 2 character long language id (LID)
    * @property-read string $Language This is a alias of {@see \BF\Locale::lid}
    * @property-read string $CID      The current 2 character long country id (CID)
    * @property-read string $Country  This is a alias of {@see \BF\Locale::cid}*
    * @property-read string $Charset  The current defined, optional character set defined by this locale.
    * @since  v0.1
    */
   final class Locale extends \BF\ExplicitGetter
   {

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   F I E L D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * The current used language (2 characters in lower case)
       *
       * @var    string
       * @since  v0.1
       */
      private $_language;

      /**
       * The current used Country (2 characters in upper case)
       *
       * @var    string
       * @since  v0.1
       */
      private $_country;

      /**
       * A optional charset defined by the locale.
       *
       * @var    string
       * @since  v0.1
       */
      private $_charset;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   S T A T I C   F I E L D S   - - - - - - - - - - - - - - -">

      /**
       * Holds the singleton instance of this object..
       *
       * @var    \BF\Locale
       * @since  v0.1
       */
      private static $instance = null;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - >   P R I V A T E   C O N S T R U C T O R   - - - - - - - - - - - - - - - - -">

      /**
       * Uses a private constructor, because its a singleton.
       *
       * @param  string $language The language id
       * @param  string $country  The optional country id
       * @param  string $charset  The optional charset
       * @since  v0.1
       */
      private function __construct( $language, $country, $charset = null )
      {

         // Init all class fields
         $this->_language = $language;
         $this->_country  = $country;
         $this->_charset  = $charset;

         // Init the array to store some usable locales
         $locales = array();

         // For windows systems we are doing this way
         if ( \IS_WIN && ( false !== ( $lcStr = \BF\LocaleHelper::ConvertLCIDToWin( (string) $this ) ) ) )
         {

            // explode the windows locale string at first underscore character (max. 2 resulting elements)
            $tmp = \explode( '_', $lcStr, 2 );

            // The LID is always element 0
            $lid = $tmp[ 0 ];

            // The CID is element 1, if defined
            $cid = ! empty( $tmp[ 1 ] ) ? $tmp[ 1 ] : '';

            // Init a empty character set. We must find it now, if defined
            $cset = '';

            // Search only for character set definition if a CID is defined
            if ( ! empty( $cid ) )
            {

               // explode at dot '.'. It separates the CID from a my defined character set
               $tmp = \explode( '.', $cid, 2 );

               // If a character set is defined
               if ( 2 === \count( $tmp ) )
               {
                  // Assigne the character set to the variable
                  $cset .= $tmp[ 1 ];
                  // Remove the charset from the CID
                  $cid   = $tmp[ 0 ];
                  // Build all usable Locales
                  $locales[] = $lid . '_' . $cid . '.' . $cset;
                  $locales[] = $lid . '-' . $cid . '.' . $cset;
                  $locales[] = $language . '_' . $country . '.' . $charset;
                  $locales[] = $language . '-' . $country . '.' . $charset;
                  $locales[] = $lid . '_' . $cid;
                  $locales[] = $lid . '-' . $cid;
                  $locales[] = $language . '_' . $country;
                  $locales[] = $language . '-' . $country;
                  $locales[] = $lid;
                  $locales[] = $language;
               }
               // There is no character set defined
               else
               {
                  // Build all usable Locales
                  $locales[] = $lid . '_' . $cid;
                  $locales[] = $lid . '-' . $cid;
                  $locales[] = $language . '_' . $country;
                  $locales[] = $language . '-' . $country;
                  $locales[] = $lid;
                  $locales[] = $language;
               }
            }
            // No usable character set
            else
            {
               $locales[] = $lid;
            }
         }
         // Here we go for non windows systems
         else
         {
            // If a charset is defined, so also a country (CID) and language (LID) must be defined
            if ( ! empty( $charset ) )
            {
               $locales[] = $language . '_' . $country . '.' . $charset;
               $locales[] = $language . '-' . $country . '.' . $charset;
               $locales[] = $language . '_' . $country;
               $locales[] = $language . '-' . $country;
            }
            // If a CID + LID is defined, but not a charset
            else if ( ! empty ( $country ) )
            {
               $locales[] = $language . '_' . $country;
               $locales[] = $language . '-' . $country;
               $locales[] = $language;
            }
            // There is only a LID defined
            else
            {
               $locales[] = $language;
            }
         }

         // Set current before defined locales only for some date+time functionality. Rest in peace… for all other :-)
         \setlocale( \LC_TIME, $locales );

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Returns the current defined 2 char language ID
       *
       * @return string
       */
      public function getLID()
      {
         return $this->_language;
      }

      /**
       * This is a alias of {@see \BF\Locale::getLID()}.
       *
       * @return string
       */
      public function getLanguage()
      {
         return $this->getLID();
      }

      /**
       * Returns the current defined 2 char country ID
       *
       * @return string
       */
      public function getCID()
      {
         return $this->_country;
      }

      /**
       * This is a alias of {@see \BF\Locale::getCID()}.
       *
       * @return string
       */
      public function getCountry()
      {
         return $this->getCID();
      }

      /**
       * Returns the current defined charset
       *
       * @return string
       * @since  v0.1
       */
      public function getCharset()
      {
         return $this->_charset;
      }

      /**
       * Overrides the magic __toString method.
       *
       * @return string
       */
      public function __toString()
      {
         return $this->_language
             .  ( ! empty( $this->_country ) ? '_' . $this->_country : '' )
             .  ( ! empty( $this->_charset ) ? '.' . $this->_charset : '' );
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Required to be private for a real singleton.
       *
       * @since  v0.1
       */
      private function __clone() { }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - - -">

      /**
       * Returns the singleton instance of this class.
       *
       * Remember: It must allready do a check, if a instance exists and create one if not so. After it returns the instance.
       * So if you will access the returned instance, hold in in a local variable.
       *
       * @return \BF\Locale
       */
      public static function GetInstance()
      {

         // If a usable instance exists, return it.
         if ( ! \is_null( self::$instance ) )
         {
            return self::$instance;
         }

         return self::Reinit();

      }

      /**
       * Reinitialize the singleton instance, configured by current {@see \BF\FrameworkConfig::$Locale}
       *
       * @param  \BF\LocaleConfig $config Youre Config. Normally there is no need to define it here.
       * @return \BF\Locale
       */
      public static function Reinit( \BF\LocaleConfig $config = null )
      {

         if ( \is_null( $config ) )
         {
            // Get the default config if no other one is defined
            $config = \BF\FrameworkConfig::GetInstance()->Locale;
         }

         foreach ( $config->InitOrder as $type )
         {

            switch ( $type )
            {
               case \BF\LocaleConfig::FIELD_TYPE_URL_PATH:
                  // If if can be successfull initialized by URL path, it ends here
                  if ( self::initByCurrentUrlPath( $config ) )
                  {
                     return self::$instance;
                  }
                  break;
               case \BF\LocaleConfig::FIELD_TYPE_REQUEST_GET:
                  // If if can be successfull initialized by GET parameter, it ends here
                  if ( self::initBySomeParameter( $config, \INPUT_GET ) )
                  {
                     return self::$instance;
                  }
                  break;
               case \BF\LocaleConfig::FIELD_TYPE_REQUEST_POST:
                  // If if can be successfull initialized by POST parameter, it ends here
                  if ( self::initBySomeParameter( $config, \INPUT_POST ) )
                  {
                     return self::$instance;
                  }
                  break;
               case \BF\LocaleConfig::FIELD_TYPE_REQUEST_SESSION:
                  // If if can be successfull initialized by SESSION parameter, it ends here
                  if ( self::initBySomeParameter( $config, \INPUT_SESSION ) )
                  {
                     return self::$instance;
                  }
                  break;
               case \BF\LocaleConfig::FIELD_TYPE_REQUEST_COOKIE:
                  // If if can be successfull initialized by COOKIE parameter, it ends here
                  if ( self::initBySomeParameter( $config, \INPUT_COOKIE ) )
                  {
                     return self::$instance;
                  }
                  break;
               case \BF\LocaleConfig::FIELD_TYPE_ACCEPT_LANG:
                  // If if can be successfull initialized by $_SERVER['HTTP_ACCEPT_LANGUAGE'], it ends here
                  if ( self::initByBrowserinfo( $config ) )
                  {
                     return self::$instance;
                  }
                  break;
               default:
                  break;
            }

         }

         // If if can be successfull initialized by current defined fallback locale self::$FallbackLocaleString, it ends here
         if ( self::initByFallback( $config ) )
         {
            return self::$instance;
         }

         // If if can be successfull initialized by current used system locale, it ends here
         if ( self::initBySystem() )
         {
            return self::$instance;
         }

         // Init by simple english locale for all other cases
         self::$instance = new \BF\Locale( 'en', '' );
         return self::$instance;

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   S T A T I C   M E T H O D S   - - - - - - - - - - - - - -">

      /**
       * @param  \BF\LocaleConfig $config
       * @return boolean
       */
      private static function initByBrowserinfo( \BF\LocaleConfig $config )
      {

         // If init by browser info is disabled, or the required $_SERVER['HTTP_ACCEPT_LANGUAGE'] is not defined
         if ( ! $config->InitFromAcceptLanguage || ! \filter_has_var( \INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE' ) )
         {
            return false;
         }

         // Format like: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
         $tmp = \explode( ',', \filter_input( \INPUT_SERVER , 'HTTP_ACCEPT_LANGUAGE' ) );

         // Iterate over each exploded element
         for ( $i = 0; $i < count( $tmp ); $i++ )
         {

            // Explode each element at first semicolon ';' to max. 2 subelements
            $tmp2 = \explode( ';', $tmp[ $i ], 2 );

            // Explode the first subelement (must be a LCID) at '-' into LID and CID
            $tmp3 = \explode( '-', $tmp2[ 0 ], 2 );

            // If last explode result not with 2 subelements explode at '_' into LID and CID
            if ( \count( $tmp3 ) < 2 )
            {
               $tmp3 = \explode( '_', $tmp2[ 0 ], 2 );
            }

            // If last explode result not with 2 subelements use only the LID
            if ( \count( $tmp3 ) < 2 )
            {
               $tmp3 = array ( $tmp2[ 0 ] );
            }
            $la   = \trim( $tmp3[ 0 ] );

            // If the LID dont use 2 characters ignore this element and restart with the next
            if ( \strlen( $la ) != 2)
            {
               continue;
            }

            // Init a empty CID
            $co = null;

            // Init a empty charset
            $cs = null;

            // If there are more than 1 subelements extracted from current element
            if ( isset( $tmp3[ 1 ] ) )
            {
               // Explode the second subelement at first pint '.'. It separates the CID from the optional character set.
               $tmp2 = \explode( '.', $tmp3[ 1 ], 2 );
               // Assign the CID
               $co   = \trim( $tmp2[ 0 ] );
               // A charset is defined, assign it
               if ( isset( $tmp2[ 1 ] ) )
               {
                  $cs = \trim( $tmp2[ 1 ] );
               }
               // clear the CID if its not defined by 2 characters
               if ( \strlen( $co ) != 2 )
               {
                  $co = null;
               }
               // Otherwise convert the CID to lower case
               else
               {
                  $co = \strtoupper ( $co );
               }
            }

            // Init the new Locale instance and return it.
            self::$instance = new \BF\Locale( $la, $co, $cs );
            return true;

         }

         // return FALSE if no usable local here was found.
         return false;

      }

      /**
       * @param \BF\LocaleConfig $config
       * @return boolean
       */
      private static function initByCurrentUrlPath( \BF\LocaleConfig $config )
      {

         // If init by current Url path is disabled, or the required $_SERVER['SCRIPT_URL'] is not defined
         if ( ! $config->InitFromUrlPath || ! \filter_has_var( \INPUT_SERVER, 'SCRIPT_URL' ) )
         {
            return false;
         }

         // IF the value of $_SERVER['SCRIPT_URL'] does not match the required data format stop this metod here
         $matches = null;
         if ( ! \preg_match( '~^/([a-zA-Z]{2})([_-]([a-zA-Z]{2}))?/~',
                             \filter_input( \INPUT_SERVER , 'SCRIPT_URL' ), $matches ) )
         {
            return false;
         }

         // Getting the LID + CID
         $lid = \strtolower( $matches[ 1 ] );
         $cid = empty( $matches[ 2 ] ) ? null : \strtoupper( $matches[ 3 ] );

         // Everything is OK. Init the new instance and return TRUE
         self::$instance = new \BF\Locale( $lid, $cid );
         return true;

      }

      /**
       * @param  \BF\LocaleConfig $config
       * @param  integer          $parameterSourceType
       * @return boolean
       */
      private static function initBySomeParameter( \BF\LocaleConfig $config, $parameterSourceType )
      {

         // If init by current request params is disabled
         if ( ! $config->InitFromRequestData || empty( $config->RequestDataParamName ) )
         {
            return false;
         }

         $definedValue = null;

         // If the require GET parameter does not exist
         if ( ! self::_isParamDefined( $config->RequestDataParamName, $parameterSourceType, $definedValue ) )
         {
            return false;
         }

         return self::_initByParam( $definedValue );

      }

      /**
       * @param  string  $name
       * @param  integer $type
       * @param  string  $definedValue
       * @return boolean
       */
      private static function _isParamDefined( $name, $type, &$definedValue )
      {

         // Extract the real chain of fieldnames. e.g.: 'foo[bar][]' results in array('foo', 'bar', 0)
         $fieldNameChain = self::_getFieldChain( $name );

         // Count the elements of the field name chain.
         $chainCount = \count( $fieldNameChain );

         if ( $chainCount < 1 || $chainCount > 4 )
         {
            return false;
         }

         // Handle the field types, that are not usable in combination with the filter functions
         switch ( $type )
         {

            case \INPUT_REQUEST:
               // Getting the value to use
               $value = self::_getFieldValue( $_REQUEST, $fieldNameChain, $chainCount );
               if ( false === $value )
               {
                  // No usable value exists, return false
                  return false;
               }
               // All is fine. We are done here.
               $definedValue = $value;
               return true;
            case \INPUT_SESSION:
               $value = self::_getFieldValue( $_SESSION, $fieldNameChain, $chainCount );
               if ( false === $value )
               {
                  // No usable value exists, return false
                  return false;
               }
               // All is fine. We are done here.
               $definedValue = $value;
               return true;
            default:
               break;
         }

         if ( \filter_has_var( $type, $fieldNameChain[ 0 ] ) )
         {
            $value = \filter_input( $type , $fieldNameChain[ 0 ] );
            if ( $chainCount === 1 )
            {
               $definedValue = $value;
               return true;
            }
            if ( ! \is_array( $value ) )
            {
               return false;
            }
            unset( $fieldNameChain[0] );
            $fieldNameChain = \array_values( $fieldNameChain );
            $value = self::_getFieldValue( $value, $fieldNameChain, $chainCount - 1 );
            if ( false === $value )
            {
               // No usable value exists, return false
               return false;
            }
            // All is fine. We are done here.
            $definedValue = $value;
            return true;
         }

         return false;

      }

      /**
       * @param  string $fieldNameDefinition
       * @return array
       */
      private static function _getFieldChain( $fieldNameDefinition )
      {
         if ( \strlen( $fieldNameDefinition ) < 1 )
         {
            return array();
         }
         if ( \preg_match( '~^[^\]\[]+$~', $fieldNameDefinition ) )
         {
            return array( $fieldNameDefinition );
         }
         $result   = array();
         $elements = explode( '[', $fieldNameDefinition );
         if ( \count( $elements ) < 2 )
         {
            return array( $fieldNameDefinition );
         }
         $result[] = $elements[ 0 ];
         for ( $i = 1; $i < \count( $elements ); ++$i )
         {
            $element = \rtrim( $elements[ $i ], ']' );
            if ( strlen( $element ) < 1 )
            {
               $element = 0;
            }
            else if ( \is_numeric( $element ) )
            {
               $element = \intval( $element );
            }
            $result[] = $element;
         }
         return $result;
      }

      /**
       * @param  array   $fields
       * @param  array   $fieldNames
       * @param  integer $chainCount
       * @return boolean
       */
      private static function _getFieldValue( array $fields, array $fieldNames, $chainCount )
      {
         if ( ! isset( $fields[ $fieldNames[ 0 ] ] ) )
         {
            return false;
         }
         if ( $chainCount === 1 )
         {
            return $fields[ $fieldNames[ 0 ] ];
         }
         if ( ! \is_array( $fields[ $fieldNames[ 0 ] ] ) )
         {
            return false;
         }
         if ( ! isset( $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ] ) )
         {
            return false;
         }
         if ( $chainCount === 2 )
         {
            return $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ];
         }
         if ( ! \is_array( $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ] ) )
         {
            return false;
         }
         if ( ! isset( $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ][ $fieldNames[ 2 ] ] ) )
         {
            return false;
         }
         if ( $chainCount === 3 )
         {
            return $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ][ $fieldNames[ 2 ] ];
         }
         if ( ! \is_array( $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ][ $fieldNames[ 2 ] ] ) )
         {
            return false;
         }
         if ( ! isset( $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ][ $fieldNames[ 2 ] ][ $fieldNames[ 3 ] ] ) )
         {
            return false;
         }
         return $fields[ $fieldNames[ 0 ] ][ $fieldNames[ 1 ] ][ $fieldNames[ 2 ] ][ $fieldNames[ 3 ] ];
      }

      /**
       * @param  string $value
       * @return boolean
       */
      private static function _initByParam( $value )
      {

         // If no usable locale value was found exit the method here
         if ( empty( $value ) )
         {
            return false;
         }

         // IF the value does not match the required data format stop this method here
         $matches = null;
         if ( ! \preg_match( '~^([a-zA-Z]{2,4})([_-]([a-zA-Z]{2,4}))?(\\.([a-zA-Z0-9][a-zA-Z0-9_-]+))?(@.+)?~',
                             $value, $matches ) )
         {
            return false;
         }

         // Create the new instance and return TRUE
         $lid = \strtolower( $matches[ 1 ] );
         $cid = empty( $matches[ 3 ] ) ? null : \strtoupper( $matches[ 3 ] );
         $cst = empty( $matches[ 5 ] ) ? null : \strtoupper( $matches[ 5 ] );
         self::$instance = new \BF\Locale( $lid, $cid, $cst );
         return true;

      }

      /**
       * @return boolean
       */
      private static function initBySystem()
      {

         // Getting the current system used locale of LC_ALL
         $lcString = \setlocale( \LC_ALL, '0' );

         // Ignore values with lower than 2 characters. It also ignores the 'C' locale
         if ( empty( $lcString ) || 2 > \strlen( $lcString ) )
         {
            return false;
         }

         // Preinitialize the local LID, CID and Charset variables
         $lid = null; $cid = null; $charset = null;

         // Handle windows different from other OS
         if ( \IS_WIN )
         {

            $tmp = \explode( ';', $lcString );

            // Loop the exploded elements
            foreach ( $tmp as $element )
            {

               // Explode at the first equal sign '='
               $tmp2 = \explode( '=', $element, 2 );

               // If the explode before results in only one element (no '=' inside $element found)
               if ( 2 > \count( $tmp2 ) )
               {
                  // Convert to LCID. If it fails ignore this $element
                  if ( false === ( $lcid = \BF\LocaleHelper::ConvertWinToLCID( $element ) ) )
                  {
                     continue;
                  }
                  // Get the elements of the current LCID
                  \BF\LocaleHelper::ExpandLCID( $lcid, $lid, $cid, $charset );
                  // Create a new instance and return TRUE
                  self::$instance = new \BF\Locale( $lid, $cid, $charset );
                  return true;
               }

               // Ignore 'LC_TYPE' locale types
               if ( 'LC_CTYPE' !== \strtoupper( $tmp2[ 0 ] ) )
               {
                  continue;
               }

               // Convert the value (after the =) to a LCID. If it fails ignore this $element
               if ( false === ( $lcid = \BF\LocaleHelper::ConvertWinToLCID( $tmp2[ 1 ] ) ) )
               {
                  continue;
               }

               // Get the elements of the current LCID
               \BF\LocaleHelper::ExpandLCID( $lcid, $lid, $cid, $charset );
               self::$instance = new \BF\Locale( $lid, $cid, $charset );
               return true;

            }

            // Return false if no usable locale is found by this way
            return false;

         }

         // No windows systems
         if ( !\preg_match( '~^[a-z]{2}([_-][a-z]{2})?(@[a-z_-]+)?(\.[a-z0-9_-]{1,14})?$~i', $lcString ) )
         {
            // A unknown locale string format
            return false;
         }

         \BF\LocaleHelper::ExpandLCID( $lcString, $lid, $cid, $charset );
         self::$instance = new \BF\Locale( $lid, $cid, $charset );
         return true;

      }

      /**
       * @param  \BF\LocaleConfig $config
       * @return boolean
       */
      private static function initByFallback( \BF\LocaleConfig $config )
      {

         // If no fallback locale string is defined => return FALSE
         if ( empty( $config->FallbackLocaleString ) )
         {
            return false;
         }

         // Init the requires elements
         $lid     = null;
         $cid     = '';
         $charset = '';

         // If the fallback locale string uses a known format
         if ( \preg_match( '~^[a-z]{2}([_-][a-z]{2})?(@[a-z_-]+)?(\.[a-z0-9_-]{1,14})?$~i',
                           $config->FallbackLocaleString ) )
         {
            // Get the required infos about LID CID and Charset
            \BF\LocaleHelper::ExpandLCID( $config->FallbackLocaleString, $lid, $cid, $charset );
            // Create a new instance and return TRUE
            self::$instance = new \BF\Locale( $lid, $cid, $charset );
            return true;
         }

         // Windows systems may need a special handling case
         if ( \IS_WIN )
         {
            // Convert the locale string to a LCID and return FALSE if it fails
            if ( false === ( $lcid = \BF\LocaleHelper::ConvertWinToLCID( $config->FallbackLocaleString ) ) )
            {
               return false;
            }
            // Get the required infos about LID CID and Charset
            \BF\LocaleHelper::ExpandLCID( $lcid, $lid, $cid, $charset );
            // Create a new instance and return TRUE
            self::$instance = new \BF\Locale( $lid, $cid, $charset );
            return true;
         }

         // Return false otherwise
         return false;

      }

      # </editor-fold>

   }

}

